#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <vector>
#include <thread>

using namespace std;



#define WIDE 40
#define HIGH 20
#define StartRow 5
#define StartCol 10
#define Esc 27
#define NumofScores 10
#define F1 0x3B
#define F2 0x3C
#define UP 0x48
#define LEFT 0x4B
#define DOWN 0x50
#define RIGHT 0x4D

typedef struct {
	int x;
	int y;
}BODY;

typedef struct {
	BODY body[HIGH * WIDE];
	int size;
}SNEAK;

typedef struct {
	int x;
	int y;
}FOOD;

COORD coord;
SNEAK sneak;
FOOD food;

int score = 0;
int addScore = 10;
int SleepTime = 250;

//void PlayBackgroundMusic(void);

void HidetheConsole(void);

void SettheConsole(int x,int y);

void InittheUI(void);

void InittheSneak(void);

void InittheFood(void);

void PlayGame(void);

vector<int> ReadScores(void);

void PreserveScores(int* isRestart,int* isPlaying);

void OutputScores(void);

void End(void);

void GameRole(void);

void DrawtheWall(void);

void Goodbye(void);

int main(void)
{
	system("title 贪吃蛇");
	//thread musicThread(PlayBackgroundMusic);
	//musicThread.detach();
	HidetheConsole();
	InittheUI();
	char getin;
	int isPlaying = 1;
	int isRestart = 1;
	while (isPlaying) {
		getin = _getch();
		switch (getin) {
			case '1':
				while (isRestart) {
					PlayGame();
					End();
					PreserveScores(&isRestart,&isPlaying);
				}
				isRestart = 1;
				break;
			case '2':
				GameRole();
				break;
			case'3':
				OutputScores();
				break;
			case Esc:
				isPlaying = 0;
				Goodbye();
				break;
			default:
				break;
				
		}
	}



	
	return 0;
}





void HidetheConsole(void)
{
	CONSOLE_CURSOR_INFO cii;
	cii.bVisible = FALSE;
	cii.dwSize = 25;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cii);
}

void SettheConsole(int x, int y)
{
	coord.X = x;
	coord.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void InittheUI(void)
{
	DrawtheWall();
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_BLUE);
	SettheConsole(2 * (15 + StartCol), 6 + StartRow);
	cout << "欢迎来到贪吃蛇！！！" << endl;
	SettheConsole(2 * (15 + StartCol), 8 + StartRow);
	cout << "按1:开始游戏" << endl;
	SettheConsole(2 * (15 + StartCol), 9 + StartRow);
	cout << "按2:查看游戏规则" << endl;
	SettheConsole(2 * (15 + StartCol), 10 + StartRow);
	cout << "按3:查看历史分数" << endl;
	SettheConsole(2 * (15 + StartCol), 11 + StartRow);
	cout << "按Esc：退出游戏" << endl;

	//Decorate the UI
	SetConsoleTextAttribute(handle, FOREGROUND_RED);
	SettheConsole(2 * (27 + StartCol), 15 + StartRow);
	cout << "●" <<endl;
	SetConsoleTextAttribute(handle, FOREGROUND_GREEN);
	SettheConsole(2 * (27 + StartCol), 12 + StartRow);
	cout << "■" << endl;
	for (int j = 11;j > 5;j--) {
		SettheConsole(2 * (27 + StartCol), j + StartRow);
		cout << "□" << endl;
	}
	SettheConsole(2 * (26 + StartCol), 6 + StartRow);
	cout << "□" << endl;
	SettheConsole(2 * (26 + StartCol), 5 + StartRow);
	cout << "□" << endl;
	SettheConsole(2 * (25 + StartCol), 5 + StartRow);
	cout << "□" << endl;
	SettheConsole(2 * (25 + StartCol), 4 + StartRow);
	cout << "□" << endl;
	for (int i = 24 + StartCol;i > 14 + StartCol;i--) {
		SettheConsole(2 * i, 4 + StartRow);
		cout << "□" << endl;
	}
	for (int j = 4 + StartRow;j > 1 + StartRow;j--) {
		SettheConsole(2 * (14 + StartCol), j);
		cout << "□" << endl;
	}
	SettheConsole(2 * (14 + StartCol), 1 + StartRow);
	cout << "□" << endl;
	SettheConsole(2 * (13 + StartCol), 1 + StartRow);
	cout << "□" << endl;
	for (int j = 1 + StartRow;j < 10 + StartRow;j++) {
		SettheConsole(2 * (12 + StartCol), j);
		cout << "□" << endl;
	}
	for (int i = 11 + StartCol;i > 2 + StartCol;i--) {
		SettheConsole(2 * i, 9 + StartRow);
		cout << "□" << endl;
	}
	
	SettheConsole(2 * 0, HIGH+1);
	cout << " ";



}

void InittheSneak(void)
{
	sneak.body[0].x = WIDE / 2 + StartCol;
	sneak.body[0].y = HIGH / 2 + StartRow;
	sneak.body[1].x = WIDE / 2 - 1 + StartCol;
	sneak.body[1].y = HIGH / 2 + StartRow;
}

void InittheFood(void)
{
	int onSneak = 0;
	do {
		onSneak = 0;
		srand(time(NULL));
		food.x = rand() % WIDE + StartCol;
		food.y = rand() % HIGH + StartRow;
		for (int i = 0;i < sneak.size;i++) {
			if (food.x == sneak.body[i].x && food.y == sneak.body[i].y) {
				onSneak = 1;
			}
		}
	} while (onSneak);
	
}

void PlayGame(void)
{
	InittheSneak();
	system("cls");
	SettheConsole(2 * sneak.body[0].x, sneak.body[0].y);
	cout << "■";
	SettheConsole(2 * sneak.body[1].x, sneak.body[1].y);
	cout << "□";
	InittheFood();
	char key = 'd';
	DrawtheWall();
	sneak.size = 2;
	int flag = 0;
	SettheConsole(2 * (StartCol + 1), HIGH + StartRow + 1);
	cout << "当前得分：" << score;
	int kx = 1, ky = 0;
	while (TRUE) {
		char tkey;
		if (_kbhit()) {
			tkey = key;
			key = _getch();
			if (key == 0xE0) {
				key = _getch();
			}
			
		}
		FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
		switch (key) {
			case'w':case UP:
				if (kx != 0 && ky != 1) {
					kx = 0;ky = -1;		break;
				}
				else {
					break;
				}
			case'a':case LEFT:
				if (kx != 1 && ky != 0) {
					kx = -1;ky = 0;		break;
				}
				else {
					break;
				}
			case's':case DOWN:
				if (kx != 0 && ky != -1) {
					kx = 0;ky = 1;		break;
				}
				else {
					break;
				}
			case'd':case RIGHT:
				if (kx != -1 && ky != 0) {
					kx = 1;ky = 0;		break;
				}
				else {
					break;
				}
			case F2:
				SleepTime += 30;
				if (addScore == 2) {
					addScore = 1;
					key = tkey;
					break;
				}
				else {
					addScore -= 2;
					key = tkey;
					break;
				}
			case F1:
				SleepTime -= 30;
				if (addScore == 1) {
					addScore = 2;
					key = tkey;
					break;
				}
				else {
					addScore += 2;
					key = tkey;
					break;
				}
			case Esc:	return;
			default:								break;
		}
		if (sneak.body[0].x + kx == sneak.body[1].x && sneak.body[0].y + ky == sneak.body[1].y) {
			flag = 1;
		}
		SettheConsole(2 * sneak.body[sneak.size - 1].x, sneak.body[sneak.size - 1].y);
		cout << " ";
		HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(handle, FOREGROUND_GREEN | FOREGROUND_RED);
		SettheConsole(2 * food.x, food.y);
		cout << "●";
		SetConsoleTextAttribute(handle, FOREGROUND_GREEN);
		for (int i = sneak.size - 1;i > 0 ;i--) {
			sneak.body[i].x = sneak.body[i - 1].x;
			sneak.body[i].y = sneak.body[i - 1].y;
			SettheConsole(2 * sneak.body[i].x, sneak.body[i].y);
			cout << "□";
		}
		sneak.body[0].x += kx;
		sneak.body[0].y += ky;
		SettheConsole(2 * sneak.body[0].x, sneak.body[0].y);
		cout << "■";
		
		if (sneak.body[0].x == food.x && sneak.body[0].y == food.y) {
			sneak.size++;
			Beep(880, SleepTime);
			if (SleepTime > 45) {
				SleepTime -= 15;
			}
			score += addScore;
			
			SettheConsole(2 * (StartCol + 1), HIGH + StartRow + 1);
			cout << "当前得分：" << score;
			InittheFood();
		}
		
		for (int i = StartCol - 1;i <= WIDE + StartCol;i++) {
			if (sneak.body[0].x == i && sneak.body[0].y == HIGH + StartRow || sneak.body[0].x == i && sneak.body[0].y == StartRow - 1) {
				flag = 1;
				break;
			}
		}
		for (int j = StartRow - 1;j <= HIGH + StartRow;j++) {
			if (sneak.body[0].x == StartCol - 1 && sneak.body[0].y == j || sneak.body[0].x == WIDE + StartCol && sneak.body[0].y == j) {
				flag = 1;
				break;
			}
		}
		for (int i = 1;i < sneak.size;i++) {
			if (sneak.body[0].x == sneak.body[i].x && sneak.body[0].y == sneak.body[i].y) {
				flag = 1;
				break;
			}
		}
		if (flag) {
			Beep(100, 500);
			return;
		}

		Sleep(SleepTime);
	}





}

vector<int> ReadScores(void)
{
	FILE* fp;
	fopen_s(&fp,"The Top 10 Scores of Sneak Game.txt", "r");
	if (fp == NULL) {
		SettheConsole(2 * (15 + StartCol), 8 + StartRow);
		cout << "读取以往成绩记录失败，失败原因：";
		perror("");
		return vector<int>();
	}
	vector<int> Scores;
	int Score;
	while (fscanf_s(fp, "%d", &Score) == 1) {
		Scores.push_back(Score);
	}
	fclose(fp);
	return Scores;
}

void PreserveScores(int* isRestart,int* isPlaying)
{
	vector<int> Scores = ReadScores();

	
	if (Scores.size() == 0) {
		SettheConsole(2 * (11 + StartCol), 8 + StartRow);
		cout << "创纪录啦！最高分被你刷新啦，真棒！！！";
		FILE* fp;
		fopen_s(&fp, "The Top 10 Scores of Sneak Game.txt", "w");
		if (fp == NULL) {
			SettheConsole(2 * (11 + StartCol), 9 + StartRow);
			cout << "存储本次成绩记录失败，失败原因：";
			perror("");
			return;
		}
		fprintf(fp, "%d ", score);
		fclose(fp);

		SettheConsole(2 * (15 + StartCol), 9 + StartRow);
		HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(handle, FOREGROUND_BLUE);
		cout << "按0：返回主菜单";
		SettheConsole(2 * (15 + StartCol), 10 + StartRow);
		cout << "按1：重新开始游戏";
		SettheConsole(2 * (15 + StartCol), 11 + StartRow);
		cout << "按Esc：结束游戏";
		char key;
		key = _getch();
		int f = 1;
		while (f) {
			switch (key) {
			case'0':
				f = 0;
				*isRestart = 0;
				score = 0;
				SleepTime = 300;
				system("cls");
				InittheUI();
				break;
			case'1':
				f = 0;
				SleepTime = 300;
				score = 0;
				break;
			case Esc:
				f = 0;
				*isRestart = 0;
				*isPlaying = 0;
				Goodbye();
				return;
			default:
				break;
			}
		}

		return;
	}



	if (score > Scores[0]) {
		SettheConsole(2 * (11 + StartCol), 8 + StartRow);
		cout << "创纪录啦！最高分被你刷新啦，真棒！！！！";
	}
	else {
		SettheConsole(2 * (12 + StartCol), 8 + StartRow);
		cout << "继续努力吧~历史最高分为: " << Scores[0];
	}

	if (Scores.size() < NumofScores) {
		Scores.push_back(score);
		for (int i = 0;i < Scores.size();i++) {
			for (int j = 0;j < Scores.size() - 1;j++) {
				if (Scores[j] < Scores[j + 1]) {
					int tem = Scores[j];
					Scores[j] = Scores[j + 1];
					Scores[j + 1] = tem;
				}
			}
		}
	}
	else {
		for (int i = 0;i < Scores.size();i++) {
			if (score > Scores[i]) {
				for (int j = Scores.size() - 1;j > i;j--) {
					Scores[j] = Scores[j - 1];
				}
				Scores[i] = score;
				break;
			}
		}
	}

	FILE* fp;
	fopen_s(&fp,"The Top 10 Scores of Sneak Game.txt", "w");
	if (fp == NULL) {
		SettheConsole(2 * (11 + StartCol), 9 + StartRow);
		cout << "存储本次成绩记录失败，失败原因：";
		perror("");
		return;
	}
	for (int i = 0;i < Scores.size();i++) {
		fprintf(fp, "%d ", Scores[i]);
	}
	fclose(fp);

	SettheConsole(2 * (15 + StartCol), 9 + StartRow);
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_BLUE);
	cout << "按0：返回主菜单";
	SettheConsole(2 * (15 + StartCol), 10 + StartRow);
	cout << "按1：重新开始游戏";
	SettheConsole(2 * (15 + StartCol), 11 + StartRow);
	cout << "按Esc：结束游戏";
	char key;
	
	int f = 1;
	while (f) {
		key = _getch();
		switch (key) {
		case'0':
			f = 0;
			*isRestart = 0;
			score = 0;
			SleepTime = 300;
			system("cls");
			InittheUI();
			break;
		case'1':
			f = 0;
			SleepTime = 300;
			score = 0;
			break;
		case Esc:
			f = 0;
			*isRestart = 0;
			*isPlaying = 0;
			Goodbye();
			return;
		default:
			break;
		}
	}
}

void OutputScores(void)
{
	system("cls");
	DrawtheWall();
	vector<int> Scores = ReadScores();
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN);
	if (Scores.size() == 0) {
		SettheConsole(2 * (15 + StartCol), 6 + StartRow);
		cout << "目前尚无游戏记录";
		return;
	}
	int i = 1;
	SettheConsole(2 * (15 + StartCol), 4 + StartRow);
	cout << "  排名" << "	" << "分数";
	for (auto it = Scores.begin();it != Scores.end();it++) {
		SettheConsole(2 * (15 + StartCol), 4 + StartRow + (i++));
		cout <<"   "<< i - 1 << "		 " << *it;
	}
	SettheConsole(2 * (12 + StartCol), 4 + StartRow + i);
	SetConsoleTextAttribute(handle, FOREGROUND_GREEN);
	cout << "以上是游戏历史分数，按下0返回主菜单";
	char getin;
	getin = _getch();
	while (TRUE) {
		if (getin == '0') {
			system("cls");
			InittheUI();
			return;
		}
	}

}

void End(void)
{
	system("cls");
	DrawtheWall();
	SettheConsole(2 * (15 + StartCol), 6 + StartRow);
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_RED);
	cout << "游戏结束";
	SettheConsole(2 * (15 + StartCol), 7 + StartRow);
	SetConsoleTextAttribute(handle, FOREGROUND_GREEN|FOREGROUND_RED);
	cout << "本次得分："<<score;
	

}

void GameRole(void)
{
	system("cls");
	cout<<R"(
		贪吃蛇游戏规则说明
		一、游戏基本信息
		本游戏为控制台版本贪吃蛇游戏，玩家通过操控蛇在限定区域内移动，达成目标并获取分数.
		二、游戏目标
		控制蛇在游戏区域内移动，通过不断吃掉随机出现的食物来增长蛇身长度，挑战尽可能高的得分，
		同时避免蛇头碰到边界或自身身体，以维持游戏进行。
		三、操作方式
		1.方向控制：使用键盘WASD键或方向键控制蛇的移动方向。
2.		2.功能键：
		F1键：加速游戏速度，每次按下，蛇的移动间隔时间减少 30 毫秒，同时每次吃食物增加的分数会根据当前情况调整。
		若当前每次吃食物增加分数为 1 分，按下F1后变为 2 分；若当前为 2 分或以上，则每次增加分数增加 2 分 。
		F2键：减速游戏速度，每次按下，蛇的移动间隔时间增加 30 毫秒，同时每次吃食物增加的分数会根据当前情况调整。
		若当前每次吃食物增加分数为 2 分，按下F2后变为 1 分；若当前为 1 分或以下，则每次增加分数减少 2 分 。
		3.暂停与退出：按下Esc键可随时退出游戏。
		四、得分与速度机制
		1.得分规则：每成功吃掉一个食物，玩家获得一定分数，初始每次吃食物增加 10 分，通过F1、F2键可调整每次
		吃食物增加的分数 。得分会实时显示在游戏界面上。
		2.速度变化：每次蛇吃掉食物后，移动速度会立即加快，表现为移动间隔时间缩短。随着吃掉食物数量的增多，蛇的移
		动速度会越来越快，对玩家的操作反应和策略规划能力要求也会越来越高。同时，玩家也可通过F1、F2键手动调整
		蛇的移动速度 。
		五、游戏结束条件
		当出现以下任意一种情况时，游戏结束：
		1.玩家按下Esc键。
		2.碰到边界：蛇头触碰到游戏区域的上下左右边界。
		3.撞到自身：蛇头碰到正在移动中的蛇身任意部位。
		4.游戏结束后，玩家可查看本局游戏最终得分，并选择终止游戏或返回主菜单。游戏结束后，系统会将本次得分与历
		史记录进行比较，若进入前 10 名，将更新历史分数记录。 
		
		以上是游戏规则，按下0返回主菜单
		)"<<endl;
	char getin;
	getin = _getch();
	while (TRUE) {
		if (getin == '0'){
			system("cls");
			InittheUI();
			return;
		}
	}

}

void DrawtheWall(void)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_RED);
	for (int i = StartCol - 1;i < WIDE + StartCol + 1;i++) {
		for (int j = StartRow - 1;j < HIGH + StartRow + 1;j++) {
			if (i == WIDE + StartCol || j == HIGH + StartRow || i == StartCol - 1 || j == StartRow - 1) {
				SettheConsole(2 * i, j);
				cout << "□";
			}
		}
	}
}

void Goodbye(void)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_RED);
	SettheConsole(2 * StartCol, HIGH + StartRow + 2);
	cout << "游戏结束，再见！";
	SettheConsole(0,0);
}




































